package com.gzhu.knowledgeAdmin.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.gzhu.knowledgeAdmin.common.constant.RoleType;
import com.gzhu.knowledgeAdmin.common.dto.*;
import com.gzhu.knowledgeAdmin.common.dto.templateFlowDiagram.EdgeAddRequest;
import com.gzhu.knowledgeAdmin.common.dto.templateFlowDiagram.EdgeDeleteRequest;
import com.gzhu.knowledgeAdmin.common.exception.BusinessException;
import com.gzhu.knowledgeAdmin.common.result.ErrorCode;
import com.gzhu.knowledgeAdmin.common.result.TreeNode;
import com.gzhu.knowledgeAdmin.common.utils.UserInfoUtils;
import com.gzhu.knowledgeAdmin.common.vo.ParamVO;
import com.gzhu.knowledgeAdmin.common.vo.TemplateExternalVO;
import com.gzhu.knowledgeAdmin.common.vo.TemplateVO;
import com.gzhu.knowledgeAdmin.common.vo.templateFlowDiagram.EdgeVO;
import com.gzhu.knowledgeAdmin.common.vo.templateFlowDiagram.NodeVO;
import com.gzhu.knowledgeAdmin.common.vo.templateFlowDiagram.TemplateFlowDiagramVO;
import com.gzhu.knowledgeAdmin.dao.TemplateMapper;
import com.gzhu.knowledgeAdmin.entity.Flow;
import com.gzhu.knowledgeAdmin.entity.Template;
import com.gzhu.knowledgeAdmin.entity.TemplateType;
import com.gzhu.knowledgeAdmin.entity.User;
import com.gzhu.knowledgeAdmin.parammanage.RecorderCache;
import com.gzhu.knowledgeAdmin.parammanage.RecorderParamCRUD;
import com.gzhu.knowledgeAdmin.repository.FlowRepository;
import com.gzhu.knowledgeAdmin.repository.TemplateRepository;
import com.gzhu.knowledgeAdmin.repository.TemplateTypeRepository;
import com.gzhu.knowledgeAdmin.service.FlowService;
import com.gzhu.knowledgeAdmin.service.TemplateService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class TemplateServiceImpl implements TemplateService {

    private static final String VARIABLE_PREFIX = "{{";
    private static final String VARIABLE_SUFFIX = "}}";

    private final String prefix = "talkmanage:template:";

    @Autowired
    private TemplateTypeRepository templateTypeRepository;

    @Autowired
    private TemplateRepository templateRepository;

    @Autowired
    private FlowRepository flowRepository;

    @Autowired
    private FlowService flowService;

    @Resource
    private RecorderParamCRUD recorderParamCRUD;

    @Resource
    private TemplateMapper templateMapper;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RecorderCache recorderCache;

    @Override
    public List<TemplateType> getTypeList() {
        return templateTypeRepository.findAll();
    }

    @Override
    public Template getTemplate(TemplateGetRequest templateGetRequest) {
        Integer templateId = templateGetRequest.getTemplateId();
        String answerType = templateGetRequest.getAnswerType();

        // 整合：查询redis缓存,命中就返回
//        Jedis jedis = new Jedis("localhost", 6379);
//        String templateJson = jedis.get("talkmanage:template:" + templateId + answerType);
        String templateJson = stringRedisTemplate.opsForValue().get(prefix + templateId + answerType);
        Template template = null;
        if (templateJson != null) {
            //缓存命中
            if (org.apache.commons.lang3.StringUtils.isNotBlank(templateJson)) {
                template = JSON.parseObject(templateJson, new TypeReference<Template>() {});
            } else {
                return template;
            }
        } else {
            //缓存未命中 从数据库读取信息
            if (StringUtils.isEmpty(answerType)) {
                // 跳转类型为空,获取当前templateId对应的话术
                Optional<Template> templateOptional = templateRepository.findById(templateId);
                if (!templateOptional.isPresent()) {
                    //不存在,抛出异常,并缓存空信息到redis(防止缓存击穿)

                    //设置缓存过期时间1小时
                    stringRedisTemplate.opsForValue().set(prefix + templateId, "", 60 * 60, TimeUnit.SECONDS);

                    throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
                }
                template = templateOptional.get();
            } else {
                //跳转类型不为空 根据templateId和answerType获取话术
                List<Flow> flowList = flowRepository.getByTemplateId(templateId);
                boolean flag = false;//标记是否找到对应的template
                for (Flow flow : flowList) {
                    String[] answerTypes = flow.getAnswerType().split(",");
                    for (String answerType1 : answerTypes) {
                        //如果answerType相等,表示找到对应的flow
                        //不相等,则继续循环
                        if (answerType.equals(answerType1)) {
                            flag = true;
                            Integer answerTarget = flow.getAnswerTarget();
                            Optional<Template> templateOptional = templateRepository.findById(answerTarget);
                            if (!templateOptional.isPresent()) {
                                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
                            }
                            //需要返回的template数据
                            template = templateOptional.get();
                            //把template对象转为json

                            String templateJSON = JSON.toJSONString(template);
                            //缓存到redis
                            stringRedisTemplate.opsForValue().set(prefix + templateId + answerType, templateJSON);
                        }
                    }
                }
                //如果没有找到,则缓存空数据,防止下一次缓存击穿
                if (!flag) {
                    //设置缓存过期时间1小时
                    stringRedisTemplate.opsForValue().set(prefix + templateId + answerType, "", 60 * 6, TimeUnit.SECONDS);
                }
            }
        }

        String variables = template.getVariables();
        if (variables != null && !variables.equals("")) {
            Map<String, String> params = templateGetRequest.getParams();
            String replaceContent = "";
            if (!params.isEmpty()) {
                replaceContent = replaceVariables(template.getContent(), variables, params);
            }
            template.setContent(replaceContent);
        }

        return template;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addTemplate(TemplateAddRequest templateAddRequest) {
        //判断beforeId与afterId,进行不同的跳转
        Integer beforeId = templateAddRequest.getBeforeId();
        Integer afterId = templateAddRequest.getAfterId();

        if (beforeId == null) {
            //则直接开启一个新的模板
            return addNewTemplate(templateAddRequest);
        }
        //若不是开启一个新的模板,则需要对以下参数进行合法性判断
        if (templateAddRequest.getAnswerType().isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "回答类型不能为空");
        }
//        String logicName = templateAddRequest.getLogicName();
//        if (StringUtils.isBlank(logicName)) {
//            throw new BusinessException(ErrorCode.PARAMS_ERROR, "逻辑顺序名不能为空");
//        }
        //type进行判断,开场白只能存在一个
        String type = templateAddRequest.getType();
        if ("开场白".equals(type)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "已经存在开场白");
        }
        if (afterId == null) {
            //直接插入到beforeId之后
            return addAfterTemplate(templateAddRequest);
        }
        //都不为空,则插入到beforeId与afterId之间
        return addBetweenTemplate(templateAddRequest);
    }

    @Override
    public List<Template> selectStatus() {
        return templateMapper.selectStatus();
    }

    @Override
    public List<TemplateExternalVO> getBeginTemplate(BeginTemplateGetRequest beginTemplateGetRequest) {
        ExampleMatcher matcher = ExampleMatcher.matching()
                //contains 模糊匹配
                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("gsid", ExampleMatcher.GenericPropertyMatchers.exact());

        Template templateQuery = new Template();
        BeanUtils.copyProperties(beginTemplateGetRequest, templateQuery);
        templateQuery.setType("开场白");

        //封装出查询实例
        Example<Template> example = Example.of(templateQuery, matcher);

        List<Template> beginTemplateList = templateRepository.findAll(example);

        List<TemplateExternalVO> templateExternalVOList = recorderParamCRUD.fillRecorderParam(beginTemplateList);


        return templateExternalVOList;
    }

    @Override
    public boolean addNewTemplate(TemplateAddRequest templateAddRequest) {

        User currentUser = UserInfoUtils.getCurrentUser();


        // 替换为真正的user类
//        Xtyh user = new Xtyh();
//        user.setGsid(currentUser.getGsid());
//        user.setGh(currentUser.getId());
//        user.setYhm(currentUser.getUserName());
//        user.setMm(currentUser.getPassword());

        String gsid = currentUser.getGsid();
//        User user = UserInfoUtil.getCurrentUser().getUser();
//        String f_gsid = user.getF_GSID();
        //TO DO 要进行name唯一的判断 唯一性:name+gsid判断
        Template firstByName = templateRepository.getFirstByNameAndGSID(templateAddRequest.getName(), gsid);
        if (firstByName != null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "话术模板名称已存在");
        }
        Template template = new Template();
        BeanUtils.copyProperties(templateAddRequest, template);
        //TO DO 新模板第一个是否要固定死为开场白
        template.setType("开场白");
        //TO DO 状态的设置
        template.setStatus(0);
        //TO DO 创建用户的设置
        template.setGsid(gsid);

        templateRepository.save(template);

        return true;
    }

    @Override
    public boolean addAfterTemplate(TemplateAddRequest templateAddRequest) {
        //先判断是否合法
        //验证beforeId对应的template是否存在
        Integer beforeId = templateAddRequest.getBeforeId();
        Optional<Template> templateOptional = templateRepository.findById(beforeId);
        if (!templateOptional.isPresent()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数错误");
        }
        Template beforeTemplate = templateOptional.get();
        //后续话术的模板名必须和开场白中的话术模板名称一致
        if (!beforeTemplate.getName().equals(templateAddRequest.getName())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数错误,模板名称不一致");
        }

        // 整合-鉴权,判断该话术模板是否是这个公司下的 管理员不受限制
        UserInfoUtils.hasOperationPermission(beforeTemplate.getGsid());

        //1.判断上级id下,是否已经存在了对应answerType
        //获取上级id下的所有跳转类型
        List<String> answerTypeList = flowService.getHasAnswerTypeById(beforeId);
        for (String answerType : templateAddRequest.getAnswerType()) {
            if (answerTypeList.contains(answerType)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "跳转类型已存在");
            }
        }
        //先封装出template对象,添加到数据库
        Template template = new Template();
        BeanUtils.copyProperties(templateAddRequest, template);
        template.setParentId(beforeId);
        //TO DO 状态设置
        template.setStatus(0);
        // TO DO 设置用户
        //整合-获取当前登陆的用户信息
        User currentUser = UserInfoUtils.getCurrentUser();


//        User user = UserInfoUtil.getCurrentUser().getUser();
        template.setGsid(currentUser.getGsid());
        //保存template数据到数据库
        templateRepository.save(template);

        //修改话术状态为禁用
//        templateRepository.updateStatus(template.getName(),0);
        templateRepository.updateStatus(template.getName(), currentUser.getGsid(), 0);

        //2.获取到封装好的对象id
        Integer targetId = template.getId();
        //3.封装出flow对象
        Flow flow = new Flow();
        flow.setTemplateId(beforeId);
        flow.setName(templateAddRequest.getName());
        //获取逻辑顺序
        Flow preFlow = flowRepository.getPreFlow(beforeId);
        int logicOrder;
        if (preFlow == null) {
            logicOrder = 1;
        } else {
            logicOrder = preFlow.getLogicOrder() + 1;
        }
        flow.setLogicOrder(logicOrder);
        flow.setLogicName(templateAddRequest.getLogicName());
        //把数组转成字符串存入数据库
        String answerType = String.join(",",templateAddRequest.getAnswerType());
        flow.setAnswerType(answerType);
        flow.setAnswerTarget(targetId);
        //TO DO 设置当前用户
        flow.setGsid(currentUser.getGsid());
        //4.添加flow到数据库
        flowRepository.save(flow);
        return true;
    }

    @Override
    public boolean addBetweenTemplate(TemplateAddRequest templateAddRequest) {
//        //1.判断beforeId,afterId对应的话术是否是存在的
//        Integer beforeId = templateAddRequest.getBeforeId();
//        Integer afterId = templateAddRequest.getAfterId();
//
//        List<Template> BAndFTemplates = templateRepository.findAllById(new ArrayList<>(Arrays.asList(beforeId, afterId)));
//        if (BAndFTemplates.size() != 2) {
//            throw new BusinessException(ErrorCode.PARAMS_ERROR);
//        }
//        Template BOrFTemplate = BAndFTemplates.get(0);
//        //后续话术的模板名必须和开场白中的话术模板名称一致
//        if (!BOrFTemplate.getName().equals(templateAddRequest.getName())) {
//            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数错误,模板名称不一致");
//        }
//        // TO DO 鉴权,判断该话术模板是否是这个公司下的 管理员不受限制
//        UserInfoUtil.hasOperationPermission(BOrFTemplate.getUser().getF_GSID());
//
//        // 要判断新增的回答类型是否已经存在,因为是增加到两个话术中间,所有新增的回答类型,可以与下一个的回答类型一样
//        //2.判断上级id下,是否已经存在了对应answerType
//        int count = flowRepository.countByTemplateIdAndAnswerTypeAndAnswerTargetNot(beforeId, templateAddRequest.getAnswerType(), afterId);
//        if (count >= 1) {
//            throw new BusinessException(ErrorCode.PARAMS_ERROR, "流程类型已存在");
//        }
//        //3.先封装出template对象,添加到数据库
//        Template template = new Template();
//        BeanUtils.copyProperties(templateAddRequest, template);
//        template.setParentId(templateAddRequest.getBeforeId());
//        //状态设置
//        template.setStatus(0);
//        //获取当前登陆用户的信息
//        User currentUser = UserInfoUtil.getCurrentUser().getUser();
//        //创建用户的设置
//        template.setUser(currentUser);
//        templateRepository.save(template);
//
//        //修改话术状态为禁用
////        templateRepository.updateStatus(template.getName(),0);
//        templateRepository.updateStatus(template.getName(), currentUser.getF_GSID(), 0);
//
//        //4.获取到新增对象id
//        Integer addTemplateId = template.getId();
//        //设置afterId的话术的父id为新增的话术的id
//        templateRepository.updateParentIdById(afterId, addTemplateId);
//        //先修改好后续flow的逻辑顺序
//        flowService.UpdateLogicOrderOnInsertBW(templateAddRequest.getName(), afterId);
//
//        //5.封装出flow对象   (新增话术指向after话术的flow)
//        Flow flow = new Flow();
//        flow.setTemplateId(addTemplateId);
//        flow.setAnswerTarget(afterId);
//        //获取指向after话术的flow,此时不可能为空
//        Flow preFlow = flowRepository.getPreFlow(afterId);
//        int logicOrder = preFlow.getLogicOrder() + 1;
//        flow.setLogicOrder(logicOrder);
//        flow.setName(templateAddRequest.getName());
//        flow.setLogicName(templateAddRequest.getLogicName());
//        flow.setAnswerType(templateAddRequest.getAnswerType());
//        //创建用户的设置
//        flow.setUser(currentUser);
//        //6.添加flow到数据库
//        flowRepository.save(flow);
//
//        //7.修改beforeId的flow中的answerTarget为新增的话术的id
//        int result = flowRepository.updateAnswerTargetByBeforeIdAndAfterId(beforeId, afterId, addTemplateId);
//        if (result <= 0) {
//            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新数据失败");
//        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTemplate(TemplateUpdateRequest templateUpdateRequest) {
        Integer id = templateUpdateRequest.getId();
        Optional<Template> templateOptional = templateRepository.findById(id);
        //数据不存在,则抛出异常
        if (!templateOptional.isPresent()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //从数据库中获取到对应的template数据
        Template template = templateOptional.get();
        //鉴权 只能修改该员工对应的公司的话术,管理员没有限制

        // 整合-鉴权,若是没有权限操作,该方法会直接抛出异常
        UserInfoUtils.hasOperationPermission(template.getGsid());

        //对名称的修改
        //名称要判断是否已经存在
        String requestName = templateUpdateRequest.getName();
        String originalName = template.getName();
        //请求中的name和根据id查询出来的template的name不一样,才进行处理
        if (!requestName.equals(originalName)) {
            //判断是要修改的name是否存在 只出一条template数据就知道是否存在
            Template templateFirstByName = templateRepository.getFirstByNameAndGSID(requestName, template.getGsid());
            if (templateFirstByName != null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "话术模板名已经存在");
            }
            //否则,进行修改,该template中的对应的name全部修改,flow中的也要修改
            templateRepository.updateName(originalName, template.getGsid(), requestName);
            flowRepository.updateName(originalName, template.getGsid(), requestName);
        }

        //获取跳转到这个id的流程Flow
        Flow flow = flowRepository.getFlowByAnswerTarget(id);
        //flow为空,说明是开场白,没有对应的flow,跳过对flow的修改
        if (flow != null) {
            //flow只能对logicName和answerType进行修改
            //当前端传过来的数据和从数据库查询出来的数据相等,就没有必要向mysql数据库发送修改语句命令
            //在jpa的save方法中,会判断值是否有修改,若是没有修改,就不发送update命令
            if (StringUtils.isEmpty(templateUpdateRequest.getLogicName()) && !templateUpdateRequest.getLogicName().equals(flow.getLogicName())) {
                //不同,才进行修改
                flow.setLogicName(templateUpdateRequest.getLogicName());
            }
            //想要修改后的answerType  List
            List<String> answerTypeRequest = templateUpdateRequest.getAnswerType();
            if (!answerTypeRequest.isEmpty()) {
                //answerType的修改,需要判断是否已经存在对应的answerType了
                //判断该话术的父话术中下,是否已经存在想要修改成的跳转条件
//                Flow hasFlow = flowRepository.getByTemplateIdAndAnswerType(flow.getTemplateId(), answerTypeRequest);
                //TODO
//                List<Flow> hasFlowList = flowRepository.getByTemplateIdAndAnswerTypeIn(flow.getTemplateId(), answerTypeRequest);
                //获取未修改前,上级id拥有的跳转类型
                List<String> hasAnswerType = flowService.getHasAnswerTypeById(flow.getTemplateId());
                //要修改的flow原本的跳转类型
                ArrayList<String> oldAnswerType = new ArrayList<>(Arrays.asList(flow.getAnswerType().split(",")));
                hasAnswerType.removeAll(oldAnswerType);
                for (String answerType : answerTypeRequest) {
                    if (hasAnswerType.contains(answerType)) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "已经存在对应的跳转类型");
                    }
                }
                //设置answerType
                flow.setAnswerType(String.join(",",answerTypeRequest));
            }
            flowRepository.save(flow);
        }

        //修改实体类中,需要修改的字段
        BeanUtils.copyProperties(templateUpdateRequest, template);

        //更新值进数据库
        templateRepository.save(template);

        //修改话术之后,话术的状态改为禁用,等待管理员审核通过之后,才能继续使用
        templateRepository.updateStatus(requestName, 0);

        this.deleteCacheByIds(Collections.singletonList(template.getId().toString()));

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(Integer id) {

        Optional<Template> templateOptional = templateRepository.findById(id);
        if (!templateOptional.isPresent()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "话术模板不存在!");
        }
        Template template = templateOptional.get();

        // 整合-鉴权 用户只能删除该公司的话术模板 管理员可以删除全部
        UserInfoUtils.hasOperationPermission(template.getGsid());

        //删除全部,只能以开场白的id为参数
        if (!"开场白".equals(template.getType())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        String name = template.getName();
        String GSID = template.getGsid();

//        int affectedRow = templateRepository.deleteTemplateByName(name);
        int affectedRow = templateRepository.deleteTemplateByNameAndGSID(name, GSID);
        if (affectedRow == 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        flowRepository.deleteFlowByNameAndGSID(name, GSID);
        this.deleteCacheByIds(Collections.singletonList(template.getId().toString()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Integer id) {
        //参数合法性判断,先判断该id对应的template是否存在
        Optional<Template> templateOptional = templateRepository.findById(id);
        if (!templateOptional.isPresent()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "话术不存在");
        }
        Template template = templateOptional.get();
        //整合—鉴权 用户只能删除该公司的话术模板 管理员可以删除全部
        UserInfoUtils.hasOperationPermission(template.getGsid());

        //然后根据template_id获取flow,若是获取为空,说明该话术后面没有对应的话术(也就是叶子节点)
        //若是有数据,则说明还节点后面是还有话术的
        List<Flow> flowList = flowRepository.getByTemplateId(id);
        //要删除templateId集合
        List<Integer> idList = null;
        if (flowList.isEmpty()) {
            //叶子节点
            //1. 先删除对应的template
            //2. 指向该节点的flow,把该flow删除
            idList = new ArrayList<>();
            idList.add(id);
        } else {
            //获取到name
            String name = flowList.get(0).getName();
            //获取到该话术和跟在该话术后面的idList
            idList = flowService.getMeAndChildTemplateId(name, id);
        }
        deleteHandler(idList);
    }

    private void deleteHandler(List<Integer> idList) {
        int affectedRow = templateRepository.deleteTemplateByIdIn(idList);
        if (affectedRow == 0) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        affectedRow = flowRepository.deleteFlowByAnswerTargetIn(idList);
        if (affectedRow == 0) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        this.deleteCacheByIds(idList.stream().map(Object::toString).collect(Collectors.toList()));
    }

    @Override
    public Page<TreeNode> list(TemplateQueryRequest templateQueryRequest) {
        //TO DO 修改TreeNode中的荷载数据字段,添加flow类型也进行返回
        //进行分页搜索
        ExampleMatcher matcher = ExampleMatcher.matching()
                //contains 模糊匹配
                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("scene", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("category", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("informType", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("gsid", ExampleMatcher.GenericPropertyMatchers.contains())
                //exact 精确匹配
//                .withMatcher("informType", ExampleMatcher.GenericPropertyMatchers.exact())
                .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact())
                //一开始只查询出  开场白话术,再根据开场白话术来查询出对应的全部话术
                .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());

        Template templateQuery = new Template();
        BeanUtils.copyProperties(templateQueryRequest, templateQuery);
        templateQuery.setType("开场白");
        if (templateQueryRequest.getStatus() != null && templateQueryRequest.getStatus().equals(-1)) {
            templateQuery.setStatus(null);
        }

        //整合-通过哪些字段判断该用户是管理员
        User currentUser = null;
        try {
            currentUser = UserInfoUtils.getCurrentUser();
        } catch (Exception e) {
            // 因为直接java调用此接口，是不用用户登录的，等伪造权限
            currentUser = new User();
            currentUser.setYhlx(RoleType.SYS_ADMIN);
        }

        //若用户不是管理员,则添加公司id的精确匹配
        if (currentUser.getYhlx() != RoleType.SYS_ADMIN) {
            //TO DO 鉴权 完成只能查询登陆用户的所属公司的话术模板 (管理员可以查看全部)
            matcher.withMatcher("f_gsid", ExampleMatcher.GenericPropertyMatchers.exact());

            //TO DO 获取到用户所在公司的id 并设置好值
            //获取当前登陆的用户信息
            //设置当前登陆用户的公司id(只能查看当前用户的公司的话术模板)
            templateQuery.setGsid(currentUser.getGsid());
        }

        //封装出查询实例
        Example<Template> example = Example.of(templateQuery, matcher);

        Pageable pageable = PageRequest.of(templateQueryRequest.getPageNum() - 1, templateQueryRequest.getPageSize());

        Page<Template> templatePage = templateRepository.findAll(example, pageable);
        //TO DO 还未完成分页功能中,对total值的赋值
        long total = templatePage.getTotalElements();
        //分页获取到的开场白话术
        List<Template> firstTemplateList = templatePage.getContent();
        //获取到name集合
        List<String> nameList = firstTemplateList.stream().map(Template::getName).collect(Collectors.toList());
        //属于该name集合的flow
        List<Flow> allFlowList = flowRepository.getFlowByNameIn(nameList);
        //进行分类(获取到的flow是所有name对应的flow)
        Map<String, List<Flow>> flowMap = allFlowList.stream().collect(Collectors.groupingBy(Flow::getName));

        //属于该name集合的template
        List<Template> allTemplateList = templateRepository.getTemplateByNameIn(nameList);
        //将获取到的List集合,转为templateId和template的映射
        HashMap<Integer, Template> allTemplateMap = new HashMap<>();
        for (Template template : allTemplateList) {
            allTemplateMap.put(template.getId(), template);
        }

        //返回结果集
        ArrayList<TreeNode> resultData = new ArrayList<>();

        for (Template template : firstTemplateList) {
            String name = template.getName();
            if (!flowMap.containsKey(name)) {
                //当只有开场白的时候,就没有flow
                TemplateVO templateVO = new TemplateVO();
                BeanUtils.copyProperties(template, templateVO);
                //设置到公司id
                templateVO.setGsid(template.getGsid());
                TreeNode treeNode = new TreeNode(template.getId(), templateVO, null);
                resultData.add(treeNode);
                continue;
            }
            List<Flow> flowList = flowMap.get(name);
            //构造出templateId和answerTarget的map映射
            HashMap<Integer, List<Integer>> map = flowService.getIdAndTargetMapByFlowList(flowList);

            TreeNode treeNode = flowService.buildTree(template.getId(), map, allTemplateMap, flowList);
            resultData.add(treeNode);
        }

        //封装出Page对象返回 TO DO 成功封装出page对象,考虑如何返回结果给前端
        PageImpl<TreeNode> page = new PageImpl<>(resultData, pageable, total);

        return page;
    }

    @Override
    public Page<TreeNode> listByCall(TemplateQueryRequest templateQueryRequest) {
        //进行分页搜索
        ExampleMatcher matcher = ExampleMatcher.matching()
                //contains 模糊匹配
                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("scene", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("category", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("informType", ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("gsid", ExampleMatcher.GenericPropertyMatchers.contains())
                //exact 精确匹配
//                .withMatcher("informType", ExampleMatcher.GenericPropertyMatchers.exact())
                .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact())
                //一开始只查询出  开场白话术,再根据开场白话术来查询出对应的全部话术
                .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());

        Template templateQuery = new Template();
        BeanUtils.copyProperties(templateQueryRequest, templateQuery);
        templateQuery.setType("开场白");
        if (templateQueryRequest.getStatus() != null && templateQueryRequest.getStatus().equals(-1)) {
            templateQuery.setStatus(null);
        }

        //封装出查询实例
        Example<Template> example = Example.of(templateQuery, matcher);

        Pageable pageable = PageRequest.of(templateQueryRequest.getPageNum() - 1, templateQueryRequest.getPageSize());

        Page<Template> templatePage = templateRepository.findAll(example, pageable);
        //TO DO 还未完成分页功能中,对total值的赋值
        long total = templatePage.getTotalElements();
        //分页获取到的开场白话术
        List<Template> firstTemplateList = templatePage.getContent();
        //获取到name集合
        List<String> nameList = firstTemplateList.stream().map(Template::getName).collect(Collectors.toList());
        //属于该name集合的flow
        List<Flow> allFlowList = flowRepository.getFlowByNameIn(nameList);
        //进行分类(获取到的flow是所有name对应的flow)
        Map<String, List<Flow>> flowMap = allFlowList.stream().collect(Collectors.groupingBy(Flow::getName));

        //属于该name集合的template
        List<Template> allTemplateList = templateRepository.getTemplateByNameIn(nameList);
        //将获取到的List集合,转为templateId和template的映射
        HashMap<Integer, Template> allTemplateMap = new HashMap<>();
        for (Template template : allTemplateList) {
            allTemplateMap.put(template.getId(), template);
        }

        //返回结果集
        ArrayList<TreeNode> resultData = new ArrayList<>();

        for (Template template : firstTemplateList) {
            String name = template.getName();
            if (!flowMap.containsKey(name)) {
                //当只有开场白的时候,就没有flow
                TemplateVO templateVO = new TemplateVO();
                BeanUtils.copyProperties(template, templateVO);
                //设置到公司id
                templateVO.setGsid(template.getGsid());
                templateVO.setAiId(recorderCache.getAiIdByRecorderName(templateVO.getRecorder()));
                TreeNode treeNode = new TreeNode(template.getId(), templateVO, null);
                resultData.add(treeNode);
                continue;
            }
            List<Flow> flowList = flowMap.get(name);
            //构造出templateId和answerTarget的map映射
            HashMap<Integer, List<Integer>> map = flowService.getIdAndTargetMapByFlowList(flowList);

            TreeNode treeNode = flowService.buildTree(template.getId(), map, allTemplateMap, flowList);
            resultData.add(treeNode);
        }

        //封装出Page对象返回 TO DO 成功封装出page对象,考虑如何返回结果给前端
        PageImpl<TreeNode> page = new PageImpl<>(resultData, pageable, total);

        return page;
    }

    @Override
    public void updateStatusByIds(List<Integer> ids, Integer status) {
        // ids为开场白的id集合,,,修改话术状态,必须先获取这些开场白对应的所有话术
        //获取到对应的name-gsid集合
        List<TemplateDTO> templateDTOByIds = templateRepository.getTemplateDTOByIds(ids);
        //鉴权问题

        //超级管理员字段的判别条件
        User currentUser = UserInfoUtils.getCurrentUser();
        if (currentUser.getYhlx() != RoleType.SYS_ADMIN) {
            // 整合-不是 超级管理员  只能修改本公司的话术状态
            String GSID = currentUser.getGsid();
//            String GSID = UserInfoUtil.getCurrentUser().getUser().getF_GSID();
            for (TemplateDTO templateDTO : templateDTOByIds) {
                if (!GSID.equals(templateDTO.getF_GSID())) {
                    //当前用户gsid和话术对应的gsid不同
                    throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
                }
            }
        }

        //根据name-gsid来修改话术状态
        List<Template> templateList = getTemplateList(templateDTOByIds);

        List<Integer> idList = templateList.stream().map(Template::getId).collect(Collectors.toList());

        templateRepository.updateStatusByIds(idList, status);

        if (status.equals(1)) {
            // 获取到flowList
            List<Flow> flowList = getFlowList(templateDTOByIds);
            //缓存数据进入redis
            handleCache(templateList, flowList);
        }
    }

    @Override
    public List<String> getTemplateVariablesById(Integer id) {
        Optional<Template> templateOptional = templateRepository.findById(id);

        if (!templateOptional.isPresent()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        String variables = templateOptional.get().getVariables();

        if (StringUtils.isEmpty(variables)) {
            return null;
        }

        ArrayList<String> list = new ArrayList<>();

        list.addAll(Arrays.asList(variables.split(",")));

        return list;
    }

    @Override
    public void cacheAllTemplate() {
        List<Template> templateList = templateRepository.findAll();
        List<Flow> flowList = flowRepository.findAll();
        handleCache(templateList, flowList);
    }

    @Override
    public List<ParamVO> getTemplateNameList() {
        User currentUser = UserInfoUtils.getCurrentUser();
        List<Template> templateList = null;
        // 系统管理员
        if (currentUser.getYhlx().equals(RoleType.SYS_ADMIN)) {
            templateList = templateRepository.getTemplateByType("开场白");
        } else {
            templateList = templateRepository.getTemplateByTypeAndGsid("开场白", currentUser.getGsid());
        }

        return templateList.stream().map(template -> {
            ParamVO paramVO = new ParamVO();
            paramVO.setId(template.getId());
            paramVO.setName(template.getName());
            return paramVO;
        }).collect(Collectors.toList());
    }

    @Override
    public TemplateFlowDiagramVO getTemplateFlowDiagram(Integer id) {
        Template templateById = templateRepository.getById(id);
        List<Template> templateByName = templateRepository.getTemplateByNameIn(Collections.singletonList(templateById.getName()));

        List<NodeVO> nodeVOList = templateByName.stream().map(template -> {
            NodeVO nodeVO = new NodeVO();
            nodeVO.setId(template.getId());
            nodeVO.setType(template.getType());
            return nodeVO;
        }).collect(Collectors.toList());

        List<Flow> flowByName = flowRepository.getFlowByNameIn(Collections.singletonList(templateById.getName()));

        List<EdgeVO> edgeVOList = flowByName.stream().map(flow -> {
            EdgeVO edgeVO = new EdgeVO();
            edgeVO.setSource(flow.getTemplateId().toString());
            edgeVO.setTarget(flow.getAnswerTarget().toString());
            edgeVO.setLabel(flow.getAnswerType());
            return edgeVO;
        }).collect(Collectors.toList());

        TemplateFlowDiagramVO templateFlowDiagramVO = new TemplateFlowDiagramVO();
        templateFlowDiagramVO.setNodes(nodeVOList);
        templateFlowDiagramVO.setEdges(edgeVOList);

        return templateFlowDiagramVO;
    }

    @Override
    public TemplateVO getTemplateById(Integer id) {
        Template template = templateRepository.getById(id);
        TemplateVO templateVO = new TemplateVO();
        BeanUtils.copyProperties(template, templateVO);
        return templateVO;
    }

    @Override
    public Integer updateTemplateInFlowDiagram(TemplateUpdateRequest templateUpdateRequest) {
        if (templateUpdateRequest.getId() != -1) {
            Template templateById = templateRepository.getById(templateUpdateRequest.getId());
            if (templateById == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }

            BeanUtils.copyProperties(templateUpdateRequest, templateById);
            templateRepository.save(templateById);
            this.deleteCacheByIds(Collections.singletonList(templateById.getId().toString()));
            return -1;
        } else {
            // -1表示该节点是新创建的
            String gsid = UserInfoUtils.getCurrentUser().getGsid();
            if ("开场白".equals(templateUpdateRequest.getType())) {
                Template firstByName = templateRepository.getFirstByNameAndGSID(templateUpdateRequest.getName(), gsid);
                if (firstByName != null) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "话术模板名称已存在");
                }
            }
            Template template = new Template();
            template.setGsid(gsid);
            BeanUtils.copyProperties(templateUpdateRequest, template);
            template.setStatus(0);
            Template saveTemplate = templateRepository.save(template);
            return saveTemplate.getId();
        }
    }

    @Override
    public void addFlowDiagramEdge(EdgeAddRequest edgeAddRequest) {
        Flow flowByTemplateIdAndAnswerTarget = flowRepository.getFlowByTemplateIdAndAnswerTarget(edgeAddRequest.getSource(), edgeAddRequest.getTarget());
        if (flowByTemplateIdAndAnswerTarget == null) {
            Template templateById = templateRepository.getById(edgeAddRequest.getSource());
            Flow flow = new Flow();
            flow.setTemplateId(edgeAddRequest.getSource());
            flow.setName(templateById.getName());
            String answerType = String.join(",", edgeAddRequest.getAnswerType());
            // TODO 处理logicName,logicOrder
            flow.setLogicName("");
            flow.setLogicOrder(1);
            flow.setAnswerType(answerType);
            flow.setAnswerTarget(edgeAddRequest.getTarget());
            flow.setGsid(UserInfoUtils.getCurrentUser().getGsid());

            flowRepository.save(flow);
        } else {
            String answerType = String.join(",", edgeAddRequest.getAnswerType());
            flowByTemplateIdAndAnswerTarget.setAnswerType(answerType);
            flowRepository.save(flowByTemplateIdAndAnswerTarget);
        }
    }

    @Override
    public void deleteFlowDiagramEdge(EdgeDeleteRequest edgeDeleteRequest) {
        int i = flowRepository.deleteFlowByTemplateIdAndAnswerTarget(edgeDeleteRequest.getSource(), edgeDeleteRequest.getTarget());
        System.out.println(i);
    }

    @Override
    public void deleteFlowDiagramNode(TemplateDeleteRequest templateDeleteRequest) {
        templateRepository.deleteById(templateDeleteRequest.getId());
        this.deleteCacheByIds(Collections.singletonList(templateDeleteRequest.getId().toString()));
    }

    private void handleCache(List<Template> templateList, List<Flow> flowList) {
        //获取id----template的映射关系
        Map<Integer, Template> templateMap = templateList.stream().collect(Collectors.toMap(Template::getId, a -> a, (k1, k2) -> k2));
        Map<String, String> resultMap = new HashMap<>();


        //保存跳转的话术
        for (Flow flow : flowList) {
            //flow中主要有templateId和answerType,answerTarget
            Integer answerTarget = flow.getAnswerTarget();
            Template answerTargetTemplate = templateMap.get(answerTarget);

            String templateJSON = JSON.toJSONString(answerTargetTemplate);
            String[] answerTypes = flow.getAnswerType().split(",");
            for (String answerType : answerTypes) {
                resultMap.put(prefix + flow.getTemplateId() + answerType, templateJSON);
            }
        }
        //保存话术本身,没跳转的话术
        for (Template template : templateList) {
            String templateJSON = JSON.toJSONString(template);
            resultMap.put(prefix + template.getId(), templateJSON);
        }

        //写入redis
        stringRedisTemplate.opsForValue().multiSet(resultMap);
    }

    /**
     * 根据key前缀批量删除
     *
     * @param templateIdList
     * @return 结果
     */
    public void deleteCacheByIds(List<String> templateIdList) {
        List<String> deleteKeyList = new ArrayList<>();
        for (String templateId : templateIdList) {
            String keyPrefix = prefix + templateId;
            if (keyPrefix != null) {
                Set<String> keys = stringRedisTemplate.keys(Pattern.matches("\\*$", keyPrefix) ? keyPrefix : keyPrefix + "*");
                deleteKeyList.addAll(keys);
            }
        }
        stringRedisTemplate.delete(deleteKeyList);
    }


    private List<Template> getTemplateList(List<TemplateDTO> templateDTOList) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Template> query = cb.createQuery(Template.class);
        Root<Template> root = query.from(Template.class);

        ArrayList<Predicate> predicates = new ArrayList<>();
        for (TemplateDTO templateDTO : templateDTOList) {
            Predicate namePredicate = cb.equal(root.get("name"), templateDTO.getName());
            Predicate gsidPredicate = cb.equal(root.get("gsid"), templateDTO.getF_GSID());
            predicates.add(cb.and(namePredicate, gsidPredicate));
        }
        query.where(cb.or(predicates.toArray(new Predicate[predicates.size()])));

        TypedQuery<Template> typedQuery = entityManager.createQuery(query);
        return typedQuery.getResultList();
    }

    private List<Flow> getFlowList(List<TemplateDTO> templateDTOList) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Flow> flowCriteriaQuery = cb.createQuery(Flow.class);
        Root<Flow> flowRoot = flowCriteriaQuery.from(Flow.class);

        ArrayList<Predicate> predicates = new ArrayList<>();
        for (TemplateDTO templateDTO : templateDTOList) {
            Predicate namePredicate = cb.equal(flowRoot.get("name"), templateDTO.getName());
            Predicate gsidPredicate = cb.equal(flowRoot.get("gsid"), templateDTO.getF_GSID());
            predicates.add(cb.and(namePredicate, gsidPredicate));
        }
        flowCriteriaQuery.where(cb.or(predicates.toArray(new Predicate[predicates.size()])));
        TypedQuery<Flow> flowTypedQuery = entityManager.createQuery(flowCriteriaQuery);
        return flowTypedQuery.getResultList();
    }

    /**
     * 把文本内容中的占位符进行值替换
     *
     * @param templateContent 文本内容
     * @param variables       变量名
     * @param valuesMap       变量名,值
     * @return 返回替换后的文本内容
     */
    private String replaceVariables(String templateContent, String variables, Map<String, String> valuesMap) {
        String[] variableNames = variables.split(",");
        for (String variableName : variableNames) {
            String variableValue = valuesMap.get(variableName);
            if (variableValue != null) {
                String variablePlaceholder = VARIABLE_PREFIX + variableName + VARIABLE_SUFFIX;
                templateContent = templateContent.replace(variablePlaceholder, variableValue);
            }
        }
        return templateContent;
    }


}
